#region Using directives
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.IO;
using RacingGame;
using RacingGame.GameLogic;
using RacingGame.Helpers;
#endregion

namespace RacingGame.Graphics
{
    public class Texture : IDisposable
    {
        #region Variables
        public static SpriteBatch alphaSprite;
        public static SpriteBatch additiveSprite;

        protected string texFilename = "";

        public string Filename
        {
            get
            {
                return texFileName;
            }
        }

        protected int texWidth, texHeight;

        public int Width
        {
            get
            {
                return texWidth;
            }
        }

        public int Height
        {
            get
            {
                return texHeight;
            }
        }

        public Rectangle GfxRectangle
        {
            get
            {
                return new Rectangle(0, 0, texWidth, texHeight);
            }
        }

        private Vector2 precaledHalfPixelSize = Vector2.Zero;

        public Vector2 HalfPixelSize
        {
            get
            {
                return precaledHalfPixelSize;
            }
        }

        protected void CalcHalfPixelSize()
        {
            precaledHalfPixelSize = new Vector2(
                (1.0f / (float)texWidth) / 2.0f,
                (1.0f / (float)texHeight) / 2.0f);
        }

        protected Texture2D internalXnaTexture;

        public virtual Texture2D XnaTexture
        {
            get
            {
                return internalXnaTexture;
            }
        }

        protected bool loaded = true;

        protected string error = "";

        public virtual bool Valid
        {
            get
            {
                return loaded &&
                       internalXnaTexture != null;
            }
        }

        protected bool hasAlpha = false;

        public bool HasAlphaPixels
        {
            get
            {
                return hasAlpha;
            }
        }
        #endregion

        #region Constructor
        public Texture(string setFilename)
        {
            if (alphaSprite == null)
                alphaSprite = new SpriteBatch(BaseGame.Device);

            if (additiveSprite == null)
                additiveSprite = new SpriteBatch(BaseGame.Device);

            if (String.IsNullOrEmpty(setFilename))
                throw new ArgumentNullException("setFilename",
                    "Unable to create texture without valid filename.");

            texFilename = Path.GetFileNameWithoutExtension(setFilename);
            string fullFilename =
                Path.Combine(Directories.ContentDirectory + "\\textures", texFilename);

            internalXnaTexture = BaseGame.Content.Load<Texture2D>(fullFilename);

            texWidth = internalXnaTexture.Width;
            texHeight = internalXnaTexture.Height;

            hasAlpha = (internalXnaTexture.Format == SurfaceFormat.Dxt5 ||
                        internalXnaTexture.Format == SurfaceFormat.Dxt3);

            loaded = true;

            CalcHalfPixelSize();
        }

        protected Texture()
        {
        }

        public Texture(Texture2D tex)
        {
            if (alphaSprite == null)
                alphaSprite = new SpriteBatch(BaseGame.Device);

            if (additiveSprite == null)
                additiveSprite = new SpriteBatch(BaseGame.Device);

            if (tex == null)
                throw new ArgumentNullException("tex");

            internalXnaTexture = tex;

            texWidth = internalXnaTexture.Width;
            texHeight = internalXnaTexture.Height;

            loaded = true;

            hasAlpha = (internalXnaTexture.Format == SurfaceFormat.Dxt5 ||
                        internalXnaTexture.Format == SurfaceFormat.Dxt3);

            CalcHalfPixelSize();
        }
        #endregion

        #region Disposing
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (internalXnaTexture != null)
                    internalXnaTexture.Dispose();
                internalXnaTexture = null;
            }

            loaded = false;
        }
        #endregion

        #region Render on screen
        public void RenderOnScreen(Rectangle rect, Rectangle pixelRect)
        {
            alphaSprite.Draw(internalXnaTexture, rect, pixelRect, Color.White);
        }

        public void RenderOnScreen(Rectangle rect,
            int pixelX, int pixelY, int pixelWidth, int pixelHeight)
        {
            alphaSprite.Draw(internalXnaTexture, rect, new Rectangle(pixelX, pixelY,
                pixelWidth, pixelHeight), Color.White);
        }

        public void RenderOnScreen(Point pos)
        {
            alphaSprite.Draw(internalXnaTexture,
                   new Rectangle(pos.X, pos.Y, texWidth, texHeight),
                   new Rectangle(0, 0, texWidth, texHeight), Color.White);
        }

        public void RenderOnScreen(Rectangle renderRect)
        {
            alphaSprite.Draw(internalXnaTexture, renderRect, GfxRectangle, Color.White);
        }

        public void RenderOnScreenRelative16To9(int relX, int relY,
                Rectangle pixelRect)
        {
            alphaSprite.Draw(internalXnaTexture, BaseGame.CalcRectangle(
                relX, relY, pixelRect.Width, pixelRect.Height),
                pixelRect, Color.White);
        }

        public void RenderOnScreenRelative4To3(int relX, int relY,
                Rectangle pixelRect)
        {
            alphaSprite.Draw(internalXnaTexture, BaseGame.CalcRectangleKeep4To3(
                relX, relY, pixelRect.Width, pixelRect.Height),
                pixelRect, Color.White);
        }

        public void RenderOnScreenRelative1600(
            int relX, int relY, Rectangle pixelRect)
        {
            alphaSprite.Draw(internalXnaTexture, BaseGame.CalcRectangle1600(
                relX, relY, pixelRect.Width, pixelRect.Height),
                pixelRect, Color.White);
        }

        public void RenderOnScreen(Rectangle rect, Rectangle pixelRect,
            Color color)
        {
            alphaSprite.Draw(internalXnaTexture, rect, pixelRect, color);
        }

        public void RenderOnScreen(Rectangle rect, Rectangle pixelRect,
            Color color, SpriteBlendMode blendMode)
        {
            if (blendMode == SpriteBlendMode.Additive)
                additiveSprite.Draw(internalXnaTexture, rect, pixelRect, color);
            else
                alphaSprite.Draw(internalXnaTexture, rect, pixelRect, color);
        }
        #endregion

        #region Rendering on screen with rotation
        public void RenderOnScreenWithRotation(
            Rectangle rect, Rectangle pixelRect,
            float rotation, Vector2 rotationPoint)
        {
            alphaSprite.Draw(internalXnaTexture, rect, pixelRect, Color.White, rotation,
                rotationPoint, SpriteEffects.None, 0);
        }
        #endregion

        #region To string
        public override string ToString()
        {
            return "Texture(filename=" + texFilename +
                ", width=" + texWidth +
                ", height=" + texHeight +
                ", xnaTexture=" + (internalXnaTexture != null ? "valid" : "null") + ")";
        }
        #endregion
    }
}
